軟體開發是充滿不確定性的工作,而不確定性通常來自於兩大方面:
上述的不確定性在新開發專案時,往往特別明顯。
新開發專案有時候並沒有讓真實的用戶來參與,因為要等到專案上線了之後,才會得到真實的用戶。換言之,最初無論寫了多詳細的規格,這些規格全都是依賴對用戶的想象而寫的,真實用戶的意見依然充滿了不確定性。
新開發專案有時也會順勢更改一些既有專案的作法:換語言、換架構、換資料庫等等。又或者是需要開發一些前所未有的新功能、整合新的函式庫、布署到新的平台上。上述的情況都會應用到新的零組件,而新的零組件很有可能文件寫得語焉不詳、缺乏範例;組件可能有 bug ,又或是依賴於某個函式庫,會與系統的其它部分產生衝突。
仔細思考不確定性的本質,其實就是缺乏關鍵的資訊,且這種資訊往往是反饋型的資訊。換言之,如果我們可以調整開發的順序或是方式,讓重要的資訊可以更快取得,這就可以讓不確定性快速地降低。
開發新專案時,Walking Skeleton 是一種可以快速降低不確定性的作法,它的概念是:「先將帶有副作用 (IO) 的零組件都整合好,之後再來開發核心邏輯。」更進一步的 Walking Skeleton 則是還包含將一行指令布署也在最初時就一併完成。
採取這個開發順序的話,一方面由於將不確定性高的零組件整合工作提前了,系統總體含有的不確定性會在專案開啟時就快速減少。更重要的的是,一旦系統有了 UI ,非開發者的協作者就可以加入專案來給予反饋;如果還可以搭配布署,那連軟體真實的用戶或是客戶的反饋也有機會提前蒐集了。
Walking Skeleton 雖然是極為簡單的作法,卻可以讓不確定快速下降,大幅減少專案的風險。
很多系統在規畫時,會發現某些需要的模組不存在,且需要的模組在別的平台上存在。這種時候,我們就可以採用移植來做為降低不確定性的作法。
移植別的平台上的模組,可以說是快速地降低『需求方向』的不確定性。一般我們談論需求時,需求一詞常有兩個意涵:一個是直接來自真實使用者的需求。另一個意涵則是由於外在的使用者需求因而需要的合理模組介面。設計合理的模組介面其實非常難,寫在書裡的一些經典介面設計,許多都是無數前人的嘗試、失敗、改善之後才取得的成果。
採取移植的作法,可以讓不確定性集中在軟體的實作與依賴關系,大幅減少不確定性。
發現某些需要的模組不存在時,除了自己重新開發之外,也可以考慮移植。
另一方面,在軟體開發的過程之中,很多時候,我們也有第三方的函式庫可以使用,然而,第三方模組雖然可以使用,卻很難用。
這會發生在什麼情況呢?比方說,使用 Clojure 這種主要支援函數式編程的語言,當我們要用到比較少見的使用情況時,就會用到第三方函式庫,且是純 Java 的函式庫。然而,Java 函式庫的特色就是會有一大堆的型別。如果直接引用的話,就會讓 Clojure 的程式碼裡充滿了 Java 的型別。
這種時候,比較好的作法是剝皮接枝,引用 Java 的函式庫之前,先一層一層地向下剝,直到找到某一層,它還沒有將資料轉化為一個又一個的型別。將該模組的上層直接剝去,並且用 Clojure 語言去將上層直接重寫過。
用這種作法既可以利用第三方的函式庫 (減少不確定性),又可以讓第三方的函式庫不會汙染整個系統。
剝皮接枝法出自一則 Arne Brasseur 的 twitter :
Much of Clojure development is peeling back layers and layers of Java libraries to get to the bit that actually matters, then wrapping that in a tiny API layer over plain data.
這些應對不確定性的模式,其實都呼應了複雜系統學家 Stuart A. Kauffman 提出的鄰近可能(Adjacent Possible)概念。
Kauffman 認為,生物系統(以及人類的經濟、文化和技術系統)的演化並非隨機跳躍,而是從當前狀態,朝向其鄰近可能空間逐步探索與擴展。這個「鄰近可能」是基於系統當前所擁有的所有組件和配置,所能達到的下一個可能狀態的集合。
例如,在生物演化中,新的基因突變或性狀,通常是基於現有的基因和環境,而不是憑空出現的。同樣地,在軟體開發中,我們可以將當前的技術棧、團隊知識和使用者反饋,視為我們的「當前狀態」。
鄰近可能的關鍵特性在於它的雙重性:它既是限制,也是機會。
在這種視角下,上述的開發模式都可以理解為系統性地管理這些「限制」與「機會」:
Walking Skeleton: 是一種透過盡早建立一個可運作的基礎,來快速探索鄰近可能的方法。它先將不確定性最高的環節(I/O、部署)穩定下來,將這些高風險的限制轉化為穩定的基石。這個新的狀態能讓團隊更安全地探索接下來的功能,並從早期反饋中找到新的「鄰近可能」方向。
Porting (移植): 則是利用外部資源來策略性地擴展鄰近可能的邊界。它將其他領域已經驗證過的解決方案,引入到我們的鄰近可能空間,直接跨越了需求方向的不確定性限制,從而跳過漫長的設計和試錯過程。
Peel and Wrap (剝皮接枝): 也是一種利用外部資源(機會)的方法,同時小心翼翼地管理外部依賴帶來的限制。它透過建立一個輕薄的 API 層,將外部組件轉化為符合自身系統風格的新工具,避免了鄰近可能空間被混亂的外部依賴所污染,確保了系統的內部一致性。
總結來說,這些策略並非單純的技術實踐,而是系統性地管理不確定性,並加速在鄰近可能空間中有效探索的方法。它們的核心思想都是:先建立一個穩定的基石,將限制轉化為機會,然後以有組織、有目標的方式,逐步擴展到下一個可行的狀態,而非盲目地向未知領域邁進。
軟體開發充滿了不確定性,主要來自於使用者需求和技術挑戰。這篇文章介紹了三種應對這種不確定性的模式:
這些模式並非單純的技術技巧,而是對不確定性的系統性管理,其核心理念與複雜系統學家 Stuart A. Kauffman 提出的「鄰近可能」(Adjacent Possible)概念不謀而合。這些策略都是透過建立一個穩定的基石,然後有組織、有目標地逐步探索和擴展,最終加速在可行的範圍內找到解決方案。